/*
 * CvUtil.h
 *
 *  Created on: 2018年3月16日
 *      Author: zhengboyuan
 */

#ifndef CVUTIL_H_
#define CVUTIL_H_

#include <opencv2/opencv.hpp>

#include <stdint.h>
#include <string>
#include <sstream>
#include <functional>


namespace cv
{

class CvUtil
{
public:
	CvUtil()
	{
	}

	virtual ~CvUtil()
	{
	}

	static void getPoints(const cv::Rect& rc, cv::Point points[4])
	{
		points[0] = rc.tl();
		points[1] = cv::Point(rc.x + rc.width, rc.y);
		points[2] = rc.br();
		points[3] = cv::Point(rc.x, rc.y + rc.height);
	}

	static std::vector< cv::Point > getPoints(const cv::Rect& rc)
	{
		std::vector< cv::Point > points;
		points.push_back(rc.tl());
		points.push_back(cv::Point(rc.x + rc.width, rc.y));
		points.push_back(rc.br());
		points.push_back(cv::Point(rc.x, rc.y + rc.height));
		return points;
	}

	static void setName(std::string& name, const char* str, void* p)
	{
		if (str == NULL || strlen(str) == 0)
		{
			std::ostringstream ss;
			ss << p;
			name = ss.str();
		}
		else
		{
			name = str;
		}
	}

	static void filterArc(std::vector<std::vector<cv::Point> >& contours, int minArc, int maxArc =0)
	{
		auto contourFilter = [=](std::vector<cv::Point>& contour) -> bool
		{
			int arc = contour.size();
			return (arc < minArc) || (maxArc > 0 && arc > maxArc);
		};
		auto newEnd = std::remove_if(contours.begin(), contours.end(), contourFilter);
		contours.erase(newEnd, contours.end());
	}

	static void filterArea(std::vector<std::vector<cv::Point> >& contours, int minArea, int maxArea=0)
	{
		auto contourFilter = [=](std::vector<cv::Point>& contour) -> bool
		{
			double area = cv::contourArea(contour);
			return (area < minArea) || (maxArea > 0 && area > maxArea);
		};
		auto newEnd = std::remove_if(contours.begin(), contours.end(), contourFilter);
		contours.erase(newEnd, contours.end());
	}

	static void filterRect(std::vector<std::vector<cv::Point> >& contours, int minRadius, int maxRadius=0)
	{
		auto contourFilter = [=](std::vector<cv::Point>& contour) -> bool
		{
			cv::Rect rect = cv::boundingRect(contour);
			if ((rect.width < minRadius) || (rect.height < minRadius))
			{
				return true;
			}
			return (maxRadius > 0) && (rect.width > maxRadius && rect.height > maxRadius);
		};
		auto newEnd = std::remove_if(contours.begin(), contours.end(), contourFilter);
		contours.erase(newEnd, contours.end());
	}

	static void filterRectRatio(std::vector<std::vector<cv::Point> >& contours, double minRatio, double maxRatio)
	{
		auto contourFilter = [=](std::vector<cv::Point>& contour) -> bool
		{
			cv::Rect rect = cv::boundingRect(contour);
			if (rect.width <= 0 || rect.height <= 0)
			{
				return true;
			}

			double ratio = 1.0;
			if (rect.width > rect.height)
			{
				ratio = ((double)rect.width) / rect.height;
			}
			else
			{
				ratio = ((double)rect.height) / rect.width;
			}

			return (ratio < minRatio || ratio > maxRatio);
		};
		auto newEnd = std::remove_if(contours.begin(), contours.end(), contourFilter);
		contours.erase(newEnd, contours.end());
	}

	/// 去除交叉重复的轮廓
	static void distinctContours(const cv::Size& size, std::vector<std::vector<cv::Point> >& contours)
	{
		cv::Mat binMat = cv::Mat::zeros(size, CV_8UC1);
		cv::Scalar color(255, 0, 0);
		for (size_t i = 0; i < contours.size(); i++)
		{
			cv::fillConvexPoly(binMat, contours[i], color);
		}

		contours.clear();

		std::vector<cv::Vec4i> hierarchy;
		cv::findContours(binMat, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
	}

};



class NamedWindow
{
public:
	explicit NamedWindow(const char* name = NULL, int flags CV_DEFAULT(CV_WINDOW_AUTOSIZE)):
		m_name(),
		m_destroyed(false)
	{
		CvUtil::setName(m_name, name, this);

		cvNamedWindow(m_name.c_str(), flags);
	}

	~NamedWindow()
	{
		destroy();
	}

	void destroy()
	{
		if (!m_destroyed)
		{
			cvDestroyWindow(m_name.c_str());
			m_destroyed = true;
		}
	}

	const char* name() const
	{
		return m_name.c_str();
	}

	void showImage(const CvArr* image) const
	{
		cvShowImage(m_name.c_str(), image);
	}

	void resizeWindow(int width, int height)
	{
		cvResizeWindow(m_name.c_str(), width, height);
	}

	void moveWindow(const char* name, int x, int y)
	{
		moveWindow(m_name.c_str(), x, y);
	}

	cv::Rect getWindowImageRect()
	{
		return cvGetWindowImageRect(m_name.c_str());
	}

	void setWindowProperty(int prop_id, double prop_value)
	{
		cvSetWindowProperty(m_name.c_str(), prop_id, prop_value);
	}

	double getWindowProperty(int prop_id)
	{
		return cvGetWindowProperty(m_name.c_str(), prop_id);
	}

	operator const char* () const
	{
		return m_name.c_str();
	}

	void setMouseCallback(CvMouseCallback on_mouse, void* param CV_DEFAULT(NULL))
	{
		cvSetMouseCallback(m_name.c_str(), on_mouse, param);
	}

protected:
	std::string	m_name;
	bool	m_destroyed;

};

class Trackbar
{
public:
	Trackbar(const char* name, const char* window, int value, int count, CvTrackbarCallback2 on_change,
		void* userdata CV_DEFAULT(0)):
		m_name(),
		m_window(),
		m_value(value)
	{
		CvUtil::setName(m_name, name, this);
		m_window = window;

		cvCreateTrackbar2(m_name.c_str(), m_window.c_str(), &m_value, count, on_change, userdata);
	}

	int value() const
	{
		return m_value;
	}

	void setValue(int pos)
	{
		m_value = pos;
	}

	const char* name() const
	{
		return m_name.c_str();
	}

	const char* window() const
	{
		return m_window.c_str();
	}

	int getPos() const
	{
		return cvGetTrackbarPos(m_name.c_str(), m_window.c_str());
	}

	void setPos(int pos) const
	{
		cvSetTrackbarPos(m_name.c_str(), m_window.c_str(), pos);
	}

	void setMin(int minval) const
	{
		cvSetTrackbarMin(m_name.c_str(), m_window.c_str(), minval);
	}

	void setMax(int maxval) const
	{
		cvSetTrackbarMax(m_name.c_str(), m_window.c_str(), maxval);
	}

	void setRange(int minval, int maxval) const
	{
		setMin(minval);
		setMax(maxval);
	}

protected:
	std::string	m_name;
	std::string	m_window;
	int	m_value;

};








} /* namespace cv */

#endif /* CVUTIL_H_ */
